2、必须是多线程使用同一把锁
弊端:多个线需要判断锁,较为消耗资源
1、明确那些代码是多线程运行代码2、明确共享数据
凡是是出现2次的变量,中间加一句睡觉,必能看见问题。
3、明确多线程运行代码中那些语句是操作共享数据的。
特点:延迟加载、多线程时容易出安全问题、同步代码块或者同步方法可解决安全问题,但效率不高同步代码块加上双重if判断,可解决效率问题
public class ThreadTest {
public static void main(String[] args) {
newThread td1=new newThread("线程A");
td1.start();
new Thread(new newThread("-----------b")).start();
}
}
class newThread extends Thread
{
newThread(String name){
super(name);
}
public void run(){
for(int i = 0 ;i<50 ; i++){
System.out.println(this.getName());
}
}
}
-------------------------------------------------1、
根据多次运行,知道
每次运行结果不一样,
这里涉及到 CPU机制问题。CPU在同一时刻,只能执行一个程序。
并迅速的在不同程序间做着切换,由于速度太快,而给人以同时运行的效果。
线程的随机性:
线程的运行,仿佛就是相互争夺 CPU的执行权利。 谁抢到执行谁,至于执行时间,看CPU。
在Java中,所有的线程都是同时启动的, 但是什么时候执行那个个,执行多久,都是CPU来决定
在Java中,每次程序的运行至少启动了2个线程,1是main主线程,2是垃圾回收线程。
在Java中,只要前台有一个线程在运行,整个Java程序进程不会消失,可以设置一个后台程序,
即使Java进程小时,后台线程依然能够运行。
2、
线程的几个方法
Static Thread currentThread() 同this 当前线程的引用。
通常 定义好一个线程后,使用内部类方式书写:
new Thread(new threadName()).start()
setName() 更改线程名
getName() 获取线程名
start() 启动
sleep() 睡眠 sleep() 用处比较广泛
Thread.sleep(6000); 设置等待6000毫秒。然后干什么。
wait() 等待
notify() 唤醒
stop() 停止
【1-2】
// 模拟卖票小程序
//继承 Thread
class MyThreadT
{
public static void main(String[] args)
{
TicketDemo t1 = new TicketDemo();
TicketDemo t2 = new TicketDemo();
TicketDemo t3 = new TicketDemo();
t2.start();
t3.start();
t1.start();
}
}
class TicketDemo extends Thread
{
private int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(this.getName()+"..."+tick--);
}
}
}
}
//2、实现 Runnable
class MyThreadR
{
public static void main(String[] args)
{
TicketDemo t = new TicketDemo();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TicketDemo implements Runnable
{
private int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
}
}
}
相同的代码,只是 一个通过继承,一个是通过实现 来达到多线程的作用。发现
继承 Thread 的线程 每一条线程都在从1到100的卖票,也就是300张票。数据
实现 Runnable 的线程 则是才卖100张票
结论:
1、避免了单继承的局限性
2、更适合多个线程去处理同一个资源。
建议使用 实现方式的 线程
区别就是 功能代码都存放在对应的run方法中。
【2】
线程的5个状态---线程的安全问题。
1、创建状态 2、运行状态 3、阻塞状态 4、消亡状态 5、冻结状态
当多个线程被创建时,在内存中会有这样一个现象:
1、都在具备运行资格,但没有执行权
2、其中一个有了执行权利,进入执行,可能会:而其他现在发生着类似情况。
1、直接运行完毕,也就是结束了。
2、运行一半 sleep
3、wait了
当属于2,3时候,当 sleep,世间结束,当 wait被 notify() 唤醒后:
1、继续获取到执行权利执行
2、返回到等待状态,拥有资格没有权利
4、运行结束或者被 stop 线程消亡
(配图)
就上述 案例中,
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
}
}
由于 线程的执行权是争取的,而执行之间是CPU 决定的。逻辑上完全可能出现下来情况:当 tick=1的时候:
1线程 判断 tick>0 进入 然后卧倒了。 这时2 线程进入 tick>0 然后又卧倒了。
然后3线程 同样进来了, 这时候也卧倒了,
三个线程都挂这里后,1线程起来了,这时候不会在判断而直接打印了当前的tick-- 也就是0
接着2 线程 起来了,也不会在判断而 打直接印了当前的tick-- 也就是-1了
接着3 线程 起来了,同样不会在判断而 打直接印了当前的tick-- 也就是-2了
打印出0 的时候 程序已经就出现问题了。
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
只需要在打印前,让该线程睡上10毫秒,即可达到上述状况也就是说,涉及到了 线程的安全问题。
问题的产生在于:当一个线程对多条语句只操作一部分的时候,另一个线程参与执行后,导致共享数据的错乱
解决办法就是: 对多条操作共享数据的语句,只让一个线程来执行完成,完成前其他线程不可参与。
Java 对于多线程的安全问题提供了对应的专业技术:
【3】
同步代码块:
synchronized(对象){
需要被同步的代码。
}
那么上面的代码就更改为:
class TicketDemo implements Runnable
{
private int tick = 100;
public void run(){
while(true){
synchronized(this)
{
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
}
}
}
}
这时候,既时写Thread.sleep(10000) 睡上一天,也不会在出现问题了。对象如同锁,持有锁的线程可以在同步中执行,
没有持有锁,即使线程获取到了CPU执行权,因为没有锁,也无法进去
联想一下 老师 ,火车的 卫生间 例子。
里面的人不出来,外面的人是进不去的。 锁,生动的解释了这一现象。
同步代码 有2个前提:
1、必须2个或以上的线程
2、必须是多个线程使用同一把锁
必须保证同步中只能有一个线程在运行。
优点:解决了多线程的安全问题
弊端:多个线需要判断锁,较为消耗资源
如何使用同步代码快?
1、明确那些代码是多线程运行代码
2、明确共享数据
凡是是出现2次的变量,中间加一句睡觉,必然能看见问题,也就是说需要同步在一起 如下:
3、明确多线程运行代码中那些语句是操作共享数据的。
同步函数、同步方法
class bank
{
public static void main(String[] args)
{
Cus b = new Cus();
new Thread(b).start();
new Thread(b).start();
}
}
class BankT
{
private int sum ;
public synchronized void add(int n)
{
sum = sum+n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(sum);
}
}
class Cus implements Runnable
{
private BankT b = new BankT();
public void run(){
for(int i = 0 ;i<3 ;i++){
b.add(100);
}
}
}
相比同步代码块而言,同步方法,更简洁一些。持有的锁为 当前对象的引用。锁 具有唯一性,那么 使用的时候,就使用当前引用,作为锁,避免出现问题
同步方法的锁是 this
同步代码块的锁 也用this ,
当同步方法和同步代码块组合应用的时候,才不会出现问题
出现问题肯定是2个前提。
当同步方法为静态的时候,锁不再是this的, 静态方法中是没有this的。
那么锁是谁呢?
因为有 static 进内存的时候是没有当前对象,所以没有this
但是一定有当前类对应的字节码文件对象
该对象的类型是 Class
class mair
{
public static void main(String[] args)
{
TicketDemo t = new TicketDemo();
new Thread(t).start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
new Thread(t).start();
}
}
class TicketDemo implements Runnable
{
//private int tick = 100; 【A】对应非静态同步方法
private static int tick = 100;
boolean flag= true;
public void run(){
if(flag){
//同步代码块
while(true){
//synchronized(this) //【C】对应A
synchronized(TicketDemo.class)
{
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...code"+tick--);
}
}
}
}else{
//同步方法
while(true){
this.show();
}
}
}
//public synchronized void show() //【B】对应A [abc 为非静态同步方法组合方式]
public static synchronized void show()
{
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...show"+tick--);
}
}
}
【重点】总结:当 静态同步方法和同步代码块组合使用的时候,锁 是 该类字节码对象
当 非静态同步方法和同步代码块组合使用时候,锁 是 当前对象引用 this
随便提一下 经常面试的【单列设计模式】题目
问;懒汉式和恶汉式有什么不同
懒汉式特点是 对象的延迟加载
问:懒汉式,有没有问题
有,多线程访问时容易出现安全问题。
问:怎么解决,
可以加同步来解决,
加同步代码块,或者同步方法 可以解决安全问题,但是效率比较低效
通过双重判断,结合同步代码块 ,可以解决效率问题
问:懒汉式的锁是那个
该类的字节码文件对象。
【重点】//懒汉式 常考 也称为 延迟加载的单例设计模式
class Single {
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
假设123,线程,1进去,判断 null ,进入同步代码块,卧倒了。2进去,同样 null ,但是 2没有锁,进不去,
1醒了创建实例对象 结束,
2醒了,进入锁,再判断, 不为 null ,出去了,
3进入,一判断,不为null */
恶汉式,常用方式
class Single{
private static final Single s = new Single();
private Single(){};
public static Single getInstance(){
return s;
}
}
死锁同步中嵌套同步
//实现一个死锁: 同步中嵌套锁
class ThreadS implements Runnable
{
private boolean falg;
ThreadS(boolean falg){
this.falg = falg;
}
public void run(){
if(falg){
synchronized(mySuo.A){
System.out.println(" if......A");
synchronized(mySuo.B){
System.out.println(" if......BBB");
}
}
}
else{
synchronized(mySuo.B){
System.out.println(" else.....BBB");
synchronized(mySuo.A){
System.out.println(" else.....AAA");
}
}
}
}
}
class mySuo // 锁
{
static Object A = new Object();//静态修饰的目的是方便调用
static Object B = new Object();
}
class sisuo
{
public static void main(String [] args){
new Thread(new ThreadS(true)).start();
new Thread(new ThreadS(false)).start();
}
}
//end