1、概念
* 线程间通信: * 多个线程操作同一个资源,但是操作的动作不同 * 例子:两个线程分别进行数据写入和取出操作,如何让两个线程协作?
public class MainActivity4 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Resource r=new Resource();
ThreadIn threadIn=new ThreadIn(r);
ThreadOut threadOut=new ThreadOut(r);
new Thread(threadIn).start();
new Thread(threadOut).start();
}
});
}
class Resource{
String name;
String sex;
}
class ThreadIn implements Runnable{
Resource r;
public ThreadIn(Resource r){
this.r=r;
}
@Override
public void run() {
int x=0;
while (true){
synchronized (r){
if (x==0){
r.name="zhangsan";
r.sex="man";
}else {
r.name="李四";
r.sex="女";
}
x=(x+1)%2;
}
}
}
}
class ThreadOut implements Runnable{
Resource r;
public ThreadOut(Resource r){
this.r=r;
}
@Override
public void run() {
while (true){
synchronized (r) {
System.out.println(r.name + "--------" + r.sex);
}
}
}
}
}
打印结果:
07-10 11:45:30.941 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.941 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.941 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.941 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.942 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.943 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.949 31359-31720/? I/System.out: zhangsan--------man
07-10 11:45:30.950 31359-31720/? I/System.out: 李四--------女
07-10 11:45:30.951 31359-31720/? I/System.out: 李四--------女
07-10 11:45:30.951 31359-31720/? I/System.out: 李四--------女
07-10 11:45:30.951 31359-31720/? I/System.out: 李四--------女
可以看出两个线程不断争夺运行权,导致连续多次输出一样的数据(out线程多次获取资源但in线程没有获取到执行权导致多次输出都是同一次输入的结果)
如果想要实现一次输入就有一次输出该如何做呢?
2、等待唤醒机制
* 等待唤醒机制实现 * --Object * -------wait(); * -------notify(); * -------notifyAll(); * 都使用在同步中,因为要对持有监视器(锁)的线程进行操作, * 所以要使用在同步中,因为只有同步才有锁的概念 * * 为什么这些操作线程的方法要定义在Object中呢? * 因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁 * 只有同一个锁上的等待线程,才能被同一个锁上的notify()唤醒 * 不能对不同锁中的线程进行唤醒 * * 也就是等待和唤醒必须是通一个锁,而锁是任意对象,可以被任意对象调用的方法定义在Object中 *
public class MainActivity4 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Resource r = new Resource();
ThreadIn threadIn = new ThreadIn(r);
ThreadOut threadOut = new ThreadOut(r);
new Thread(threadIn).start();
new Thread(threadOut).start();
}
});
}
class Resource {
private String name;
private String sex;
private boolean flag = false;
private synchronized void set(String name, String sex) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
private synchronized void out() {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + "--------" + sex);
flag = false;
this.notify();
}
}
class ThreadIn implements Runnable {
Resource r;
public ThreadIn(Resource r) {
this.r = r;
}
@Override
public void run() {
int x = 0;
while (true) {
if (x == 0) {
r.set("zhangsan", "man");
} else {
r.set("李四", "女");
}
x = (x + 1) % 2;
}
}
}
class ThreadOut implements Runnable {
Resource r;
public ThreadOut(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.out();
}
}
}
}
打印结果:
07-10 14:59:18.051 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.051 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.051 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.051 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.052 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.052 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.052 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.052 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.053 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.053 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.053 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.053 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.054 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.054 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.054 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.054 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.054 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.055 2511-2583/? I/System.out: zhangsan--------man
07-10 14:59:18.055 2511-2583/? I/System.out: 李四--------女
07-10 14:59:18.055 2511-2583/? I/System.out: zhangsan--------man
3、
* 线程间通信:生产者消费者 * * 当有多个生产者线程和多个消费者线程时如何处理? * * 对于多生产者多消费者为什么使用while判断标记? * 原因:被唤醒的线程要再一次进行标记判断 * * 为什么定义notifyAll? * 因为需要唤醒对方线程, * 如果使用notify,会出现只唤醒本方线程的情况,导致程序中所有的线程都会处于等待状态。 *
public class MainActivity4 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Resource r = new Resource();
Producer producer = new Producer(r);
Producer producer2 = new Producer(r);
Consumer consumer = new Consumer(r);
Consumer consumer2 = new Consumer(r);
//多个生产者
new Thread(producer).start();
new Thread(producer2).start();
//多个消费者
new Thread(consumer).start();
new Thread(consumer2).start();
}
});
}
class Resource {
private String name;
private int number = 1;
private boolean flag = false;
private synchronized void set(String name) {
while (flag) {//多个消费者生产者时使用--循环判断(当线程开始执行时必须判断flag)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name +":"+ (number++);
System.out.println(Thread.currentThread().getName() + "++++++++生产了+++++" + this.name);
flag = true;
this.notifyAll();//多个消费者生产者时使用--唤醒所有的等待线程(如果只唤醒消费者线程或者只唤醒生产者线程就会出现所有线程都进入到等待状态)
}
private synchronized void out() {
while (!flag) {//多个消费者生产者时使用----循环判断
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "----------------消费了-------------" + this.name);
flag = false;
this.notifyAll();//多个消费者生产者时使用--唤醒所有的等待线程
}
}
class Producer implements Runnable {
Resource r;
public Producer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.set("商品");
}
}
}
class Consumer implements Runnable {
Resource r;
public Consumer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.out();
}
}
}
}
4、以上代码会唤醒所有线程,但实际上只是想唤醒对方线程(因为唤醒本方线程只会让这个线程再次进入等待状态,消耗资源),该如何实现呢?
* JDK1.5提供了多线程的升级解决方案 * synchronized--->显示的Lock操作 lock、unlock * Object中的wait、notify、notifyAll--->Condition中的await、signal、signalAll * * 以下示例中实现了本方只唤醒对方的操作。
public class MainActivity4 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Resource r = new Resource();
Producer producer = new Producer(r);
Producer producer2 = new Producer(r);
Consumer consumer = new Consumer(r);
Consumer consumer2 = new Consumer(r);
//多个生产者
new Thread(producer).start();
new Thread(producer2).start();
//多个消费者
new Thread(consumer).start();
new Thread(consumer2).start();
}
});
}
class Resource {
private String name;
private int number = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_producer = lock.newCondition();
private Condition condition_consumer = lock.newCondition();
private void set(String name) {
lock.lock();
try {
while (flag) {//多个消费者生产者时使用--循环判断(当线程开始执行时必须判断flag)
condition_producer.await();
}
this.name = name + ":" + (number++);
System.out.println(Thread.currentThread().getName() + "++++++++生产了+++++" + this.name);
flag = true;
condition_consumer.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
//释放锁的操作一定要执行
} } private void out() { lock.lock(); try { while (!flag) {//多个消费者生产者时使用----循环判断 condition_consumer.await(); } System.out.println(Thread.currentThread().getName() + "----------------消费了-------------" + this.name); flag = false; condition_producer.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } class Producer implements Runnable { Resource r; public Producer(Resource r) { this.r = r; } @Override public void run() { while (true) { r.set("商品"); } } } class Consumer implements Runnable { Resource r; public Consumer(Resource r) { this.r = r; } @Override public void run() { while (true) { r.out(); } } }}
以上方式和之前方式相比有效率上的提升
老的方式
07-10 16:50:41.338 22961-23023/com.example.gradledemo I/System.out: Thread-4811++++++++生产了+++++商品:1
7-10 16:50:42.268 22961-23025/? I/System.out: Thread-4813----------------消费了-------------商品:2838
耗时:0.93s
新的方式
07-10 16:58:47.632 24484-25467/com.example.gradledemo I/System.out: Thread-4841++++++++生产了+++++商品:1
07-10 16:58:48.361 24484-25469/com.example.gradledemo I/System.out: Thread-4843----------------消费了-------------商品:2838
耗时:0.729s
5、多线程--停止线程
* 多线程:停止线程 * stop();已过时。 * 如何停止线程呢? * 只有一种方式:run方法结束。 * 开启多线程运行,通长都是循环结构, * 只要控制住循环,就可以让run方法结束,也就是线程结束。 * <p> * <p> * 特殊情况: * 当线程处于冻结状态时,就不会读取到标记,线程就不会结束。 * * * 当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结的线程进行清除。 * 强制让冻结线程恢复到运行状态,这样就可以操作标记让线程结束 * Thread中提供的方法是:interrupt
public class MainActivity4 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StopThread dieThread = new StopThread();
Thread t1=new Thread(dieThread);
t1.start();
Thread t2=new Thread(dieThread);
t2.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
int num = 0;
while (true) {
if (num++ == 10) {
dieThread.setFlag(false);
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "===正在运行==" + num);
}
}
});
}
class StopThread implements Runnable {
boolean flag = true;
private void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public synchronized void run() {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() +"==="+ e.getMessage());
flag=false;
}
System.out.println(Thread.currentThread().getName() + "===正在运行---");
}
}
}
}
总结:a、线程无法手动关闭(stop方法过时)
b、线程关闭==run方法结束
c、当线程处在冻结状态时,通过 intercept方法让线程回到运行状态,再通过标记控制来结束线程。
6、多线程--守护线程
* Marks this thread as either a {@linkplain #isDaemon daemon} thread * or a user thread. The Java Virtual Machine exits when the only * threads running are all daemon threads. * * <p> This method must be invoked before the thread is started.
Thread-----setDaemon(boolean on) 将线程标记为守护线程,当正在运行的线程都是守护线程时,jam退出,该方法必须在启动线程前调用。 思考:
setDaemon在Android中的使用
7、多线程--join方法
* Waits for this thread to die. * * <p> An invocation of this method behaves in exactly the same * way as the invocation thread.join();//当thread线程结束的时候再执行上一个线程 join可以用来临时加入线程
8、多线程--线程优先级
thread.setPriority(Thread.MAX_PRIORITY);