Java 多线程
Thread
sample
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
//.....
}
}
class Main {
public static void main(String[] args) {
MyThread a = new MyThread('A');
MyThread b = new MyThread('B');
a.start();
b.start();
}
}
start 方法不能重复调用, 重复调用会出现java.lang.IllegalThreadStateException异常
Runnable
sample
class MyRunnable extend Runnable{
public MyRunnable() {
}
@Override
public void run() {
}
}
class Main {
public static void main(String[] args) {
MyRunnable a = new MyRunnable('A');
MyRunnable b = new MyRunnable('B');
Thread demoa = new Thread(a);
Thread demob = new Thread(b);
demoa.start();
demob.start();
}
}
关于选择继承Thread还是实现Runnable接口?
其实Thread也是实现Runnable接口的:
class Thread implements Runnable {
//…
public void run() {
if (target != null) {
target.run();
}
}
}
Thread中的run方法调用的是Runnable接口的run方法。Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。
Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
class MyThread implements Runnable{
private int ticket = 5; //5张票
public void run() {
for (int i=0; i<=20; i++) {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);
}
}
}
}
public class lzwCode {
public static void main(String [] args) {
MyThread my = new MyThread();
new Thread(my, "1号窗口").start();
new Thread(my, "2号窗口").start();
new Thread(my, "3号窗口").start();
}
}
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
主线程和子线程之间的关系
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM就是在操作系统中启动了一个进程。
主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。
// 休眠两秒
Thread.sleep(2000);
// 强制加入
thread.join();
// 后台线程
thread.setDaemon(true);
/**
* @author Rollen-Holt 后台线程
* */
class hello implements Runnable {
// 虽然有一个死循环,但是程序还是可以执行完的。因为在死循环中的线程操作已经设置为后台运行了。
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + "在运行");
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he, "线程");
demo.setDaemon(true);
demo.start();
}
}
// 优先级
// 不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源。
thread.setPriority(8);
// 礼让
// 在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。
Thread.currentThread().yield();
同步和死锁
/**
* @author Rollen-Holt
* */
class hello implements Runnable {
public void run() {
for(int i=0;i<10;++i){
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
public static void main(String[] args) {
hello he=new hello();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
private int count=5;
}
//【运行结果】:
// 5
// 4
// 3
// 2
// 1
// 0
// -1
这里出现了-1,显然这个是错的。应该票数不能为负值。
如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,其他的线程必须等到这个线程结束之后才能继续执行。
使用线程同步解决问题
使用同步代码块
和同步方法
同步代码块
synchronized(同步对象){
//需要同步的代码
}
/**
* @author Rollen-Holt
* */
class hello implements Runnable {
public void run() {
for(int i=0;i<10;++i){
synchronized (this) {
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
}
public static void main(String[] args) {
hello he=new hello();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
private int count=5;
}
同步方法
synchronized 方法返回类型方法名(参数列表){
// 其他代码
}
/**
* @author Rollen-Holt
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 10; ++i) {
sale();
}
}
public synchronized void sale() {
if (count > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count--);
}
}
public static void main(String[] args) {
hello he = new hello();
Thread h1 = new Thread(he);
Thread h2 = new Thread(he);
Thread h3 = new Thread(he);
h1.start();
h2.start();
h3.start();
}
private int count = 5;
}
死锁
当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。
生产者,消费者问题
/*
*面包类,用于存放厨师生产的面包
*/
public class Bread {
private String producer;
public Bread(String producer) {
super();
this.producer = producer;
}
@Override
public String toString() {
return producer;
}
}
/*
* 篮子类,用于存放面包
* 篮子假定最多放10个面包
*/
public class Basket {
private int index = 0;
private Bread[] arrayBread = new Bread[10];
/*
* 此方法用于往篮子里扔面包 每当厨师生成好一个面包就往篮子里边扔 由于当某一个厨师在往篮子扔面包的过程(还没扔完,但是面包已经在篮子里),
* 又有一个厨师要往篮子里扔面包。 如果这是篮子里已经有9个面包的话,最后一个厨师就不能在扔了。
* 所以需要给这个方法加把锁,等一个厨师扔完后,另外一个厨师才能往篮子里扔。
*/
public synchronized void push(int id, Bread bread) {
System.out.println("生成前篮子里有面包:" + index + " 个");
// 当厨师发现篮子满了,就在那里不停的等着
while (index == arrayBread.length) {
System.out.println("篮子满了,我开始等等。。。。。。");
try {
// 厨师(一个生产线程)开始不停等待
// 他需要等待顾客(一个消费线程)把它叫醒
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒一个正在等待的线程,如果唤醒的线程为生产线程,则又会进入等待状态,
// 如果为消费线程,则因生产线程生产了面包的缘故,消费线程可以进行消费
this.notify();
arrayBread[index] = bread;
index++;
System.out.println(bread);
}
/*
* 此方法用于往篮子里拿面包 加锁原因和上边一样
*/
public synchronized Bread pop(int id) {
System.out.println("消费前篮子里有面包:" + index + " 个");
while (index == 0) {
System.out.println("篮子空了,我开始等等。。。。。。");
try {
// 顾客(一个消费线程)开始不停等待
// 他需要等待厨师(一个生产线程)把它叫醒
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒一个正在等待的线程,如果唤醒的线程为消费线程,则又会进入等待状态,
// 如果为生产线程,则因生产线程消费了面包的缘故,生产线程可以进行生产
this.notify();
index--;
System.out.println("第" + id + "个顾客消费了 -->" + arrayBread[index]);
return arrayBread[index];
}
}
/*
* 厨师类,用于生产面包
*/
public class Kitchener implements Runnable {
private Basket basket;
private int id;
public Kitchener(int id,Basket basket) {
super();
this.id = id;
this.basket = basket;
}
@Override
public void run() {
//让厨师生产10个面包
for (int i = 1; i <= 10; i++) {
Bread bread = new Bread("第" + id + "个厨师生成的面包");
basket.push(id,bread);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/*
* 顾客类,用于消费面包
*/
public class Customer implements Runnable {
private Basket basket;
private int id;
public Customer(int id,Basket basket) {
super();
this.id = id;
this.basket = basket;
}
@Override
public void run() {
// 让顾客消费10个面包
for (int i = 1; i <= 10; i++) {
basket.pop(id);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
Basket basket = new Basket();
// 两个厨师两个客户
Kitchener kitchener1 = new Kitchener(1,basket);
Kitchener kitchener2 = new Kitchener(2,basket);
Customer customer1 = new Customer(1,basket);
Customer customer2 = new Customer(2,basket);
new Thread(kitchener1).start();
new Thread(kitchener2).start();
new Thread(customer1).start();
new Thread(customer2).start();
}
}
名字和年龄对应错误了,
http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。