Java---多线程(下)

1. 线程同步

Java允许多线程并发进行,当多个线程对同一对象执行操作时将会导致数据不准确,相互之间产生冲突,这时加入同步锁以避免在该线程操作完成之前被其他线程调用。
(1)同步方法

  • 静态方法,锁对象为该类的字节码对象(如果用引用数据类型的成员变量当作锁对象,必须是静态的)
  • 非静态方法,锁对象为this
final Solution solution = new Solution();
        new Thread() {
            @Override
            public void run() {
                while (true)
                solution.print1();
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                while (true)
                solution.print2();
            }
        }.start();
    }
}
class Solution {
    public synchronized void print1() {
            System.out.print("我");
            System.out.print("爱");
            System.out.print("学");
            System.out.print("习\r\n");
    }
    public void print2() {
        synchronized (this) {//若为静态方法则为Solution.class
            System.out.print("我");
            System.out.print("爱");
            System.out.print("玩");
            System.out.print("游");
            System.out.print("戏\n\r");
        }
    }

(2)同步代码块

  • 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
  • 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
  • 锁对象是任意的,但是不能使用匿名对象,因为每次创建的都是不同的对象
    public static void main(String[] args)  {
        final Solution solution = new Solution();
        new Thread() {
            @Override
            public void run() {
                while (true)
                solution.print1();
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                while (true)
                solution.print2();
            }
        }.start();
    }
}
class Solution {
    Object o = new Object();
    public void print1() {
        synchronized (o) {
            System.out.print("我");
            System.out.print("爱");
            System.out.print("学");
            System.out.print("习\r\n");
        }
    }
    public void print2() {
        synchronized (o) {
            System.out.print("我");
            System.out.print("爱");
            System.out.print("玩");
            System.out.print("游");
            System.out.print("戏\n\r");
        }
    }

(3)使用特殊域变量实现同步
(4)使用重入锁实现同步

2.线程通信

多个线程并发执行,CPU随机切换,如果希望他们有规律的执行,则需要实现线程间的通信

两个线程之间的通信
  • 如果希望线程等待, 就调用wait()
  • 如果希望唤醒等待的线程, 就调用notify();
  • 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
public static void main(String[] args)  {
         final Solution solution = new Solution();
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        solution.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        solution.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
class Solution {
    private int num = 1;
    public void print1() throws InterruptedException {
        synchronized (this) {
            if(num != 1) {
                    this.wait();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("学");
            System.out.print("习\r\n");
            num = 2;
            this.notify();   //随机唤醒单个线程
        }
    }

    public void print2() throws InterruptedException {
        synchronized (this) {
            if (num != 2) {
                    this.wait();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("玩");
            System.out.print("游");
            System.out.print("戏\n\r");
            num = 1;
            this.notify();
        }
    }
多个线程之间的通信
public static void main(String[] args)  {
         final Solution solution = new Solution();
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        solution.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        solution.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        solution.print3();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
class Solution {
    private int num = 1;
    public void print1() throws InterruptedException {
        synchronized (this) {
            while(num != 1) {
               this.wait();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("学");
            System.out.print("习\r\n");
            num = 2;
            this.notifyAll();
        }
    }

    public void print2() throws InterruptedException {
        synchronized (this) {
            while(num != 2) {
                    this.wait();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("玩");
            System.out.print("游");
            System.out.print("戏\n\r");
            num = 3;
            this.notifyAll();
        }
    }
    public void print3() throws InterruptedException {
        synchronized (this) {
            while(num != 3) {
                this.wait();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("看");
            System.out.print("书\r\n");
            num = 1;
            this.notifyAll();
        }
    }
线程间的通信需要注意的问题

(1)在同步代码块中,用哪个对象锁,就用哪个对象调用wait()
(2)notifyAll(),wait(),notify()为什么定义在object这个类中?
锁对象是任意对象,而object类是所有类的基类,所以定义在这个类中。
(3)sleep()和wait()方法的区别:
sleep()必须传入参数,参数就是时间,时间到了自然醒来
wait()可以传入参数,也可以不传入参数,传入参数就是在参数时间结束后等待
(2)sleep()方法在同步代码块和同步函数中不释放锁,CPU仍然在此线程
wait()方法在同步代码块中释放锁

互斥锁
  • 1.同步使用ReentrantLock类的lock()【获取锁】和unlock()【释放锁】方法进行同步
  • 2.通信使用ReentrantLock类的newCondition()方法可以获取Condition对象需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程
class Solution {
    private ReentrantLock r = new ReentrantLock();
    private Condition c1 = r.newCondition();
    private Condition c2 = r.newCondition();
    private Condition c3 = r.newCondition();
    private int num = 1;

    public void print1() throws InterruptedException {
        r.lock();
        if (num != 1) {
            c1.await();
        }
        System.out.print("我");
        System.out.print("爱");
        System.out.print("学");
        System.out.print("习\r\n");
        num = 2;
        c2.signal();
    r.unlock();
}

    public void print2() throws InterruptedException {
        r.lock();
            if(num != 2) {
                c2.await();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("玩");
            System.out.print("游");
            System.out.print("戏\n\r");
            num = 3;
            c3.signal();
        r.unlock();
    }
    public void print3() throws InterruptedException {
        r.lock();
            if(num != 3) {
                c3.await();
            }
            System.out.print("我");
            System.out.print("爱");
            System.out.print("看");
            System.out.print("书\r\n");
            num = 1;
            c1.signal();
        r.unlock();
    }
}

线程组
  • Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
  • 默认情况下,所有的线程都属于主线程组。
  • public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
  • public final String getName()//通过线程组对象获取他组的名字
  • 我们也可以给线程设置分组
  • 1,ThreadGroup(String name) 创建线程组对象并给其赋值名字
  • 2,创建线程对象
  • 3,Thread(ThreadGroup?group, Runnable?target, String?name)
  • 4,设置整组的优先级或者守护线程
线程池的使用
  • 线程池的概述:程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
  • 内置线程池的使用:JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
    (1)public static ExecutorService newFixedThreadPool(int nThreads)
    (2)public static ExecutorService newSingleThreadExecutor()
    (3)这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
    (4)Future<?> submit(Runnable task)
    (5) Future submit(Callable task)
  • 创建步骤:
    (1)创建线程池对象
    (2)创建Runnable实例
    (3)提交Runnable实例
    (4)关闭线程池
public static void main(String[] args) {
        ExecutorService e = Executors.newFixedThreadPool(2); 
        e.submit(new Solution());
        e.submit(new Solution());
        e.shutdown();                                     //关闭线程池
    }
}
class Solution implements Runnable{
    @Override
    public void run() {
        for (int i = 100;i >= 0;i--) {
     System.out.println(Thread.currentThread().getName() + "····" + i );
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值