进程:是一个正在执行中的程序。 每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程。
JavaVM 启动的时候会有一个进程java.exe.
该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
1,如何在自定义的代码中,自定义一个线程呢?
创建线程的方法有两种:
第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
3,调用线程的start方法, 该方法两个作用:启动线程,调用run方法。
示例代码如下:
- class Demo extends Thread
- {
- public void run()
- {
- for(int x=0; x<60; x++)
- System.out.println("demo run----"+x);
- }
- }
- class ThreadDemo
- {
- public static void main(String[] args)
- {
- //for(int x=0; x<4000; x++)
- //System.out.println("Hello World!");
- Demo d = new Demo();//创建好一个线程。
- //d.start();//开启线程并执行该线程的run方法。
- d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
- for(int x=0; x<60; x++)
- System.out.println("Hello World!--"+x);
- }
- }
为什么要覆盖run方法呢?
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
第二种方法:Java给我们提供了一个规则。Runnable接口。
如果自定义类不继承Thread,也可以实现Runnable接口。并将多线程要运行的代码存放在Runnable的run方法中。所以也可以实现Runnable接口来定义线程。
步骤:
1,定义了实现Runnable接口。
2,覆盖接口的run方法。将多线程要运行的代码存入其中。
3,创建Thread类的对象(创建线程),并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
为什么要传递?因为线程要运行的代码都在Runnable子类的run方法中存储。所以要将该run方法所属的对象
传递给Thread。让Thread线程去使用该对象调用其run方法。
4,调用Thread对象的start方法。开启线程。程序如下:
示例代码如下:
- package xuexi1;
- public class ThreadDemo1 {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- ticket1 t=new ticket1();
- Thread t1=new Thread(t);
- Thread t2=new Thread(t);
- Thread t3=new Thread(t);
- Thread t4=new Thread(t);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- }
- }
- class ticket1 implements Runnable{
- private static int ticket=100;
- @Override
- public void run() {
- // TODO Auto-generated method stub
- while(true){
- synchronized(new Object()){
- if(ticket>0){
- System.out.println(Thread.currentThread().getName()+"......"+ticket--);
- }
- }
- }
- }
- }
实现方式和继承方式有什么区别呢?
继承Thread: 线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
因为避免了单继承的局限性,所以创建线程建议使用第二种方式。
为什么运行结果每一次都不同?因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
多线程具备随机性。因为是由cpu不断的快速切换造成的。就有可能会产生多线程的安全问题。
问题的产生的原因:
1,多线程代码中有操作共享数据。
2,多条语句操作该共享数据。
有一个线程对多条操作共享数据的代码执行的一部分。还没有执行完,另一个线程开始参与执行。
就会发生数据错误。
解决方法:
当一个线程在执行多条操作共享数据代码时,其他线程即使获取了执行权,也不可以参与操作。
java就对这种解决方式提供了专业的代码同步。
同步的原理:就是将部分操作功能数据的代码进行加锁。
同步使用的前提:
1,必须是两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
这是才可以称为这些线程被同步了。
同步的示例代码:
- package xuexi1;
- public class InputOutputDemo2 {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Resource1 r1=new Resource1();
- new Thread(new Input1(r1)).start();
- new Thread(new Output1(r1)).start();
- new Thread(new Input1(r1)).start();
- new Thread(new Output1(r1)).start();
- }
- }
- class Resource1{
- private String name;
- private String sex;
- boolean flag=false;
- public synchronized void set(String name,String sex){
- if(flag){
- try{
- this.wait();
- }
- catch(Exception e){
- System.out.println(e.toString());
- }
- }
- this.name=name;
- this.sex=sex;
- flag=true;
- this.notifyAll();
- }
- public synchronized void get(){
- if(!flag){
- try{
- this.wait();
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
- else{
- System.out.println(this.name+"....."+this.sex+"***"+Thread.currentThread().getName());
- flag=false;
- this.notifyAll();
- }
- }
- }
- class Input1 implements Runnable{
- Resource1 res;
- Input1(Resource1 res){
- this.res=res;
- }
- @Override
- public void run(){
- int i=0;
- while(true){
- if(i==0)
- res.set("123","456");
- else if(i==1){
- res.set("ggggg","asdf");
- }
- i=(i+1)%2;
- }
- }
- }
- class Output1 implements Runnable{
- Resource1 res;
- Output1(Resource1 res){
- this.res=res;
- }
- public void run() {
- while(true){
- res.get();
- }
- }
- }
1.5版本后用lock()和unlock()来同步。并且用condition来激活和冻结指定的这样就避免了死锁或者用notifyAll()来激活所用的线程。示例代码如下:
- package xuexi1;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- public class ProductConsumerDemo1 {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Resource4 res =new Resource4();
- productor4 pro=new productor4(res);
- consumor4 con=new consumor4(res);
- new Thread(pro).start();
- new Thread(pro).start();
- new Thread(con).start();
- new Thread(con).start();
- }
- }
- class Resource4{
- private int count=0;
- private String name ;
- private boolean flag;
- private Lock lock=new ReentrantLock();
- private Condition pro=lock.newCondition();
- private Condition con=lock.newCondition();
- public void produce(String name) throws InterruptedException{
- lock.lock();
- try{
- while(flag)
- pro.await();
- this.name=name+count++;
- System.out.println("producer..."+this.name+".."+Thread.currentThread().getName());
- flag=true;
- con.signal();
- }finally{
- lock.unlock();
- }
- }
- public void consum(){
- lock.lock();
- try{
- while(!flag){
- try {
- con.await();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- System.out.println("consumor,,,,,,,,,,,,,"+this.name+".."+Thread.currentThread().getName());
- flag=false;
- pro.signal();
- }finally{
- lock.unlock();
- }
- }
- }
- class productor4 implements Runnable{
- private int i=0;
- private Resource4 res;
- productor4(Resource4 res){
- this.res=res;
- }
- public void run() {
- while(true){
- try {
- if(i==0)
- res.produce("123");
- else if(i==1)
- res.produce("asddff");
- i=(i+1)%2;
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- class consumor4 implements Runnable{
- private Resource4 res;
- consumor4(Resource4 res){
- this.res=res;
- }
- public void run(){
- while(true){
- res.consum();
- }
- }
- }
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt()方法来解决这一问题。如下面的代码:
- class StopThread implements Runnable
- {
- private boolean flag =true;
- public void run()
- {
- while(flag)
- {
- System.out.println(Thread.currentThread().getName()+"....run");
- }
- }
- public void changeFlag()
- {
- 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.setDaemon(true);
- t2.setDaemon(true);
- t1.start();
- t2.start();
- int num = 0;
- while(true)
- {
- if(num++ == 60)
- {
- st.changeFlag();
- t1.interrupt();
- t2.interrupt();
- break;
- }
- System.out.println(Thread.currentThread().getName()+"......."+num);
- }
- System.out.println("over");
- }
- }