-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
1、进程:是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做控制单元。
2、线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。
3、一个进程至少有一个线程。
4、JVM在启动时会有一个进程java.exe。该线程中至少一个线程负责java程序运行,而这个线程运行的代码存在于main方法中,该线程称为主线程
5、扩展:其实更细节说明JVM,jvm启动不止一个线程,还有负责垃圾回收机制的线程
二、创建线程
创建新执行线程有两种方法。
1、一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法(将自定义代码存储在run方法中),在调用线程start()方法。该方法有两个作用:启动线程和调用run方法。(如果调用的是run方法,仅仅是对象的调用。线程创建了但是并没有开启)
2、实现Runnable接口。
1)定义实现Runnable接口
2)覆盖Runnable接口中的run方法()
3)通过Thread类建立线程对象
4)将Runable接口的子类对象作为实际参数传递给Thread类的构造函数
5)调用Thread类的start方法,开启线程,并调用Runnable接口子类的run方法
3、实现方式和继承方式有什么区别呢?
1)实现方式的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式
2)继承Thread:线程代码存放Thread子类run方法中
实现Runnable,线程代码存在接口子类的run方法中
三、线程运行状态
四:获取线程对象及名称
1、线程都有自己默认的名称:Thread-编号,该编号从0开始
2、名称可以初始化父类构造方法(Thread(String name)),super(name);
3、Thread.currentThread();获取当前线程对象
getName():获取线程名称
设置线程名称:setName或者构造函数初始化
五:多线程安全问题
1、当多条语句在操作同一个共享数据时,一条线程对多条语句只执行了一部分,没有执行完,另一条线程参与进来执行。导致共享数据的错误
2、解决方法:对多条操作操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其它线程不可以参与执行
3、java对多线程安全问题提供了专业的解决方法——就是同步代码块:
synchronized(对象)(对象如同锁,持有锁得线程可以在同步中执行)
{
需要被同步的代码;(看哪些代码在操作共享数据)
}
4、同步的前提:
1)必须有两个或者两个以上的线程
2)必须是多个线程使用同一个锁
5、如何明确代码需要同步:
1)明确哪些代码是多线程运行代码
2)明确共享数据
3)明确多线程运行代码中哪些语句是操作共享数据的
6、同步的好处是解决多线程安全问题;弊端是多个线程需要判断锁,较为消耗资源
7、同步函数:使用的锁是this
8、静态同步函数:用于的锁是 类名.class 。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象——类名.class 该类型的对象是Class
9:死锁:
示例:买票程序
/*
多个窗口买票程序
*/
class Ticket implements Runnable
{
int ticket = 4000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(ticket>0)
{
try
{
Thread.sleep(5);
}
catch (Throwable e)
{
}
System.out.println(Thread.currentThread().getName()+"....."+ticket--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t =new Ticket();
Thread t1 = new Thread(t).start();
Thread t2 = new Thread(t).start();
Thread t3 = new Thread(t).start();
Thread t4 = new Thread(t).start();
}
}
示例解释:单例懒汉式同步知识点
懒汉式代码
<span style="font-size:14px;">class Single
{
private static Single s = null;
private Single(){}//将构造函数私有化,其它类不能对其建立对象
public static Single getInstance()//对外提供访问接口
{
if(s==null)
{
synchronized (Single.class)//静态同步函数的锁是其字节码文件对象
{
if(s==null)//加上双重判断解决多线程等待后重复new对象
s = new Single;
}
}
return s;
}
}
</span>
1、汉式和饿汉式有什么不同:懒汉式的特点是实例的延迟加载2、懒汉式延迟加载有没有问题? :有,如果多线程访问时容易出现安全问题
3、怎么解决:可以加同步来解决,用同步函数和同步代码块都行,但是稍微有些低效。用双重判断和同步代码块可以解决这个问题
4、加同步的时候使用的锁是哪一个:该类所属的字节码文件对象——Single.class
死锁程序(要掌握)
/*
线程间死锁
同步中嵌套同步,而锁却不同
*/
class SiSuo implements Runnable
{
private boolean flag;
SiSuo(Boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(Lock.locka)
{
System.out.println("if locka" );
synchronized(Lock.lockb)
{
System.out.println("if lockb" );
}
}
}
}
else
{
while(true)
{
synchronized(Lock.lockb)
{
System.out.println("else locka" );
synchronized(Lock.locka)
{
System.out.println("else lockb" );
}
}
}
}
}
}
class Lock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class SiSuoDemo
{
public static void main(String[] args)
{
Thread t1 = new Thread(new SiSuo(true));
Thread t2 = new Thread(new SiSuo(false));
t1.start();
t2.start();
}
}
六线程间通信
1、待唤醒机制:
2、wait()、notify()、notifyAll()都使用在同步中。因为要对持有监视器(锁)的线程操作,所以要使用在同步中
3、等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object中
示例:
/*
多线程 生产者,消费者
生产一个消费一个
*/
class Resouce
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)//使用同步函数,同步锁是this
{
while(flag)//循环判断标记,为真就等待,如果此处使用if会造成生产2个使用一个的情况。当t1、t2线程都在此等待时
try{this.wait();}catch(Exception e){}//下一次被唤醒并获得执行权时,会直接执行下面语句。所以要用while循环
this.name=name+"...."+count++;
System.out.println(Thread.currentThread().getName()+"....生产者...."+this.name);
flag=true;//执行完一次后将标记反向
this.notifyAll();//唤醒所有的等待线程
}
public synchronized void get()//同步函数
{
while(!flag)//循环判断标记,非真就等待,此处使用if会造成生产一个使用2个的情况。当t3、t4线程都在此等待时
try{this.wait();}catch(Exception e){}//下一次被唤醒的时候不会判断if语句,而是直接执行下面语句
System.out.println(Thread.currentThread().getName()+"....消费者................"+this.name);
flag=false;
this.notifyAll();//唤醒等待线程
}
}
class Produser implements Runnable
{
private Resouce r;//创建一个类类型引用变量
Produser(Resouce r)
{
this.r=r;
}
public void run()//调用线程执行run方法
{
while(true)
{
r.set("商品");
}
}
}
class Consumer implements Runnable
{
private Resouce r;
Consumer(Resouce r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.get();
}
}
}
class ProduserConsumerDemo
{
public static void main(String[] args)
{
Resouce r = new Resouce();
Produser p =new Produser(r);
Produser p1=new Produser(r);
Consumer c = new Consumer(r);
Consumer c1 = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p1);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c1);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
七 线程停止
1、如何停止线程:使run方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束
2、特殊情况:当线程处于冻结状态,就不会读取标记,那么下次就不会结束
解决方式:(调用interrupt方法)当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
线程停止程序示例:
/*
1、如何停止线程:使run方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束
2、特殊情况:当线程处于冻结状态,run方法里面有wait等,就不会读取标记,那么下次就不会结束
解决方式:(调用interrupt方法)当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
*/
class StopThread implements Runnable
{
private boolean flag = true;
public synchronized void run()
{
while(flag)
{
try
{
wait();//线程等待,并释放执行权
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+"......Exception");//打印当前异常的线程名
flag = false;
}
System.out.println(Thread.currentThread().getName()+"......run");
}
}
public void change()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
//t1.Daemon(true)//将t1 t2设置为守护线程,即后台线程,当前台线程如主线程运行结束
//t2.Daemon(true)//后,后台线程也结束运行Java虚拟机退出。
t1.start();
t2.start();
int x = 0;
while(true)
{
if(x++==60)
{
//st.change();//改变线程标记来停止线程。在一种特殊情况下这两个线程也不会结束,
//就是当run方法中加上同步时,这时就不能采取这种方式让线程停止了
t1.interrupt();//当run方法中加上同步,且线程处于等待状态时,就必须使用interrupt方法
//来对冻结的线程进行唤醒,再改吧循环标记,使线程停止
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"......run");
}
System.out.println("end");
}
}
八 守护线程
setDaemon(boolean on)
当线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用九、Join方法
1:、当A线程执行到了B线程的 .Join()方法时,A就会等待,等B线程都执行完,A才会执行。
2、Join可以用来临时加入线程执行
/*
join方法
当线程A执行到线程B的join方法时,A就会等待。等B线程都执行完,A才会执行
join可以用来临时加入线程执行
*/
class Join implements Runnable
{
public void run()
{
for(int x =0;x<60;x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class JoinDemo
{
public static void main(String[] args)throws InterruptedException
{
Join j = new Join();
Thread t1 = new Thread(j);
Thread t2 = new Thread(j);
t1.start();
//t1.join();//当t1调用了join方法,就会抢夺主线程执行权,主线程进入等待状态,t1执行完后主线程恢复向下执行,这时t2与主线程交替运行
t2.start();
t1.join();//当t2在t2线程后面时,t1、t2交替运行,t1执行完后,t2就与主线程交替运行
for(int y = 0;y<70;y++)
{
System.out.println(Thread.currentThread().getName()+"...."+y);
}
System.out.println("end");
}
}
十 优先级 & yield 方法
yield暂停当前正在执行的线程对象,并执行其他线程。
开发多线程一般写法示例:
/*
开发时多线程一般写法
采用匿名内部类的写法
*/
class ThreadTest
{
public static void main(String[] args)
{
new Thread()
{
public void run()
{
for(int x =0;x<2000;x++)
{
System.out.println(Thread.currentThread().getName()+"...."+x);
}
}
}.start();
for(int x =0;x<2000;x++)
{
System.out.println(Thread.currentThread().getName()+"...."+x);
}
Runnable r = new Runnable()
{
public void run()
{
for(int x =0;x<2000;x++)
{
System.out.println(Thread.currentThread().getName()+"...."+x);
}
}
};
new Thread(r).start();
}
}