线程

进程:

正在运行的程序,是系统进行资源分配和调用的独立单位;

每一个进程都有他自己的内存空间和系统资源。


线程:(线程是依赖于进程而存在;线程是程序的执行单元;

是进程的单个顺序控制流,是一条执行路径;

一个进程如果只有一条执行路径,则称为单线程;

一个进程如果有多条执行路径,则称为多线程。


多线程:

优点:

1)适当的提高程序的执行效率(多个线程同时执行)。
2)适当的提高了资源利用率(CPU、内存等)。
缺点:
1)占用一定的内存空间。
2)线程越多CPU的调度开销越大。
3)程序的复杂度会上升。

并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。

并发:物理上同时发生,指在某一个时间点同时运行多个程序。


线程调度:

分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。

抢占式调度模型:优先让优先权高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。

(一般采用的是抢占式优先)


线程默认的优先级是5;

线程的优先级的范围是:1-10;


● 线程

创建线程的两种方式:

一、继承Thread类,扩展线程。

class DemoThread extends Thread {

    @Override
    public void run() {
        super.run();
        // Perform time-consuming operation...
    }
}

DemoThread t = new DemoThread();
t.start();

  • 继承Thread类,覆盖run()方法。
  • 创建线程对象并用start()方法启动线程。
  • 3)Thread 类中的 start() 和 run() 方法有什么区别?
  • 调用 start() 方法才会启动新线程;如果直接调用 Thread 的 run() 方法,它的行为就会和普通的方法一样;为了在新的线程中执行我们的代码,必须使用 Thread.start() 方法。

    public class Test {
        public static void main(String[] args)  {
            MyThread thread = new MyThread();
            thread.start();
        }
    }
    class MyThread extends Thread{
        private static int num = 0;
        public MyThread(){
            num++;
        }
        @Override
        public void run() {
            System.out.println("主动创建的第"+num+"个线程");
        }
    }
    区别start()方法和run()方法:
public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}
 
class MyThread extends Thread{
    private String name;
 
    public MyThread(String name){
        this.name = name;
    }
 
    @Override
    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
}



  • 二、实现 Runnable 接口。

在Java中创建线程除了继承Thread类之外,还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。


public class Test {
    public static void main(String[] args)  {

        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
} 
class MyRunnable implements Runnable{
    public MyRunnable() {
    }
 
    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}

Runnable的中文意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。

事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。

在Java中,这2种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。











线程休眠:public static void sleep(long millis)




线程加入:public final void join()



线程礼让:public static void yield()





后台线程:public final void setDaemon(boolean on)




线程中断:

public final void stop()

public void interrupt()



不安全(stop)






线程的生命周期:






线程中,要设置锁,但是要避免死锁的问题



实现100张票三个窗口同时卖票

public class SellTicket  implements Runnable{
	
	//定义100张票
	private int tickets = 100;
	//创建锁对象
	private Object obj = new Object();
		public void run(){
		     
private synchronized void SellTicket{
while(true) {
		    	     	 
		        if(tickets>1){
					try{
				 		Thread.sleep(100);
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售"+(tickets--)+"张票");
				}

			}

} }
public static void main(String[] args){
//创建资源对象SellTicket st = new SellTicket();
//创建3个线程对象
Thread t1=new Thread(st,"窗口1");
Thread t2=new Thread(st,"窗口2");
Thread t3=new Thread(st,"窗口3");
//启动线程t1.start();t2.start();t3.start();}}

 

方法二:将synchronize写在方法体上

private synchronized void SellTicket{
while(true) {
		    	     	 
		        if(tickets>1){
					try{
				 		Thread.sleep(100);
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售"+(tickets--)+"张票");
				}

			}
} }
 

 
 
解决线程安全问题的基本思想:是否是多线程环境;是否有共享数据;是否有多条语句操作共享数据;解决线程安全问题:synchronized(同步代码块):同步对象可以解决安全问题的根本原因就在对象上,该对象如同锁的功能。 

public class ThreadDemo {

	public static void main(String[] args) {
		// 线程安全的类
		StringBuffer sb=new StringBuffer();
		Vector<String> v=new Vector<String>();
		Hashtable<String,String> h=new Hashtable<String,String>();
		
		//
		List<String> list1=new ArrayList<String>();  //线程不安全
		
		List<String> list2=Collections.synchronizedList(new ArrayList<String>());//线程安全
	}

}
public class ThreadDemo {

	public static void main(String[] args) {
		// 线程安全的类
		StringBuffer sb=new StringBuffer();
		Vector<String> v=new Vector<String>();
		Hashtable<String,String> h=new Hashtable<String,String>();
		
		//
		List<String> list1=new ArrayList<String>();  //线程不安全
		
		List<String> list2=Collections.synchronizedList(new ArrayList<String>());//线程安全
	}

}

同步的前提:

多个线程;
多个线程使用的是同一个锁对象;
同步的好处:
同步的出现解决了多线程的安全问题。
同步的弊端:
当线程相当多的时候,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中或降低程序的运行效率。
死锁问题:
死锁是指在两个或两个以上的线程在执行过程中,因争夺资源产生的一种互相等待现象。

Lock锁的使用:
void lock();  //获取锁
void unlock(); //释放锁

	//定义100张票
		private int tickets = 100;
		//定义锁对象
		private Lock lock = new ReentrantLock();
		
			public void run(){
				
			     while(true) { 
			    	 try{
			    	 //加锁
			    	 lock.lock();
			    	 
			        if(tickets>1){
						try{
							Thread.sleep(100);
						}catch(InterruptedException e){
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+"正在出售"+(tickets--)+"张票");
					}			        
				}finally{
					//释放锁
			        lock.unlock();
				}
			} 
	}
}
 
 
 

采用Runnable接口方式实现

public class Sellticket implements Runnable {
    //定义一百张票
    private int tickets = 100;
    @Override
    public void run() {
        while (true){
            if (tickets > 0 ){
                System.out.println(Thread.currentThread().getName() + "正在出售" + (tickets--) +"张票");
            }
        }
    }
}
public class RunnableDemo {
    public static void main(String[] args){
        /*
    多线程Runnable接口方式卖电影票
    */
        //创建资源对象
        Sellticket st = new Sellticket();

        //创建三个线程对象
        Thread t1 = new Thread(st,"窗口1");
        Thread t2 = new Thread(st,"窗口2");
        Thread t3 = new Thread(st,"窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

采用Runnable接口方式实现,但是每次卖票延迟100毫秒

改进后的电影院售票出现问题:

1.相同的票出现多次
CPU的一次操作必须是原子性的
2.还出现了负数的票
随机性和延迟导致的

解决线程安全问题的基本思想:

首先想为什么出现问题?(也是我们判断是否有问题的标准)
是否是多线程环境
是否有共享数据
是否有多条语句操作共享数据
如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境。
怎么实现呢?
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。


解决线程安全问题实现1:

同步代码块
格式:
synchronized(对象){需要同步的代码;}
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。(多线程必须是同一把锁)


同步的前提
多个线程
多个线程使用的是同一个锁对象
同步的好处
同步的出现解决了多线程的安全问题。
同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。


解决线程安全问题实现2:

同步方法
就是把同步关键字加到方法上
同步方法的锁对象是什么呢?(this)


如果是静态方法,同步方法的锁对象又是什么呢?(类的字节码文件(类名.class))
那么,我们到底使用谁?
如果锁对象是this,就可以考虑使用同步方法。
否则能使用同步代码块的尽量使用同步代码块。


public class Sellticket implements Runnable {
    //定义一百张票
    private int tickets = 100;
    //定义锁对象
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized(obj) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售" + (tickets--) + "张票");
                }
            }
        }
    }
}
public class RunnableDemo {
    public static void main(String[] args){
        /*
    多线程Runnable接口方式卖电影票
    */
        //创建资源对象
        Sellticket st = new Sellticket();

        //创建三个线程对象
        Thread t1 = new Thread(st,"窗口1");
        Thread t2 = new Thread(st,"窗口2");
        Thread t3 = new Thread(st,"窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

同步弊端
效率低
如果出现了同步嵌套,就容易产生死锁问题
死锁问题及其代码
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
同步代码块的嵌套案例


线程间通信:针对同一个资源的操作有不同种类的线程

案例:通过设置线程(生产者)和获取线程(消费者)针对同一个学生对象进行操作

注意:要考虑线程安全问题

public class Student {
        private String name;
        private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class SetThread implements Runnable{
    private Student s;
    public int x = 0;
    public SetThread(Student s){
        this.s = s;
    }
    @Override
    public void run() {
        while (true){
            synchronized (s){
                if (x % 2 == 0){
                    s.setName("林青霞");
                    s.setAge(27);
                }else{
                    s.setName("Fairy");
                    s.setAge(20);
                }
                x ++;
            }
        }
    }
}

public class GetThread implements Runnable {
    private Student s;
    public GetThread(Student s){
        this.s = s;
    }
    @Override
    public void run() {
        while (true){
            synchronized (s){
                System.out.println(s.getName() +"------"+s.getAge());
            }
        }
    }
}

public class StudentDemo {
    public static void main(String[] args){
        //创建资源
        Student s = new Student();

        //设置和获取类
        SetThread setThread = new SetThread(s);
        GetThread getThread = new GetThread(s);

        //线程类
        Thread t1 = new Thread(setThread);
        Thread t2 = new Thread(getThread);

        t1.start();
        t2.start();

    }
}

生产者和消费者正常思路:

A:生产者:

先看是否有数据,有就等待,没有就生产,生产完之后通知消费者来消费数据

B:消费者:

先是看是否有数据,有就消费,没有就等待,通知生产者生产数据

为了处理这样的问题,java就提供了一种机制:等待唤醒机制

等待唤醒:

Object类中提供了三个方法:

wait():等待

notify():唤醒单个线程

notifyAll():唤醒所有线程

public class Student {
        private String name;
        private int age;

        boolean flag; // 默认是没有数据的

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class SetThread implements Runnable{
    private Student s;
    public int x = 0;
    public SetThread(Student s){
         this.s = s;
    }
    @Override
    public void run() {
        while (true){
            synchronized (s){
                //判断有没有数据
                if(s.flag){
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (x % 2 == 0){
                    s.setName("林青霞");
                    s.setAge(27);
                }else{
                    s.setName("Fairy");
                    s.setAge(20);
                }
                x ++;

                //修改标记
                s.flag = true;
                //唤醒线程
                s.notify();
            }
        }
    }
}
public class GetThread implements Runnable {
    private Student s;
    public GetThread(Student s){
        this.s = s;
    }
    @Override
    public void run() {
        while (true){

            synchronized (s){
                if (!s.flag){
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(s.getName() +"------"+s.getAge());
                //修改标记
                s.flag = false;
                //唤醒线程
                s.notify();
            }

        }
    }
}
public class StudentDemo {
    public static void main(String[] args){
        //创建资源
        Student s = new Student();

        //设置和获取类
        SetThread setThread = new SetThread(s);
        GetThread getThread = new GetThread(s);

        //线程类
        Thread t1 = new Thread(setThread);
        Thread t2 = new Thread(getThread);

        t1.start();
        t2.start();

    }
}

线程组

Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。
public final ThreadGroup getThreadGroup()
我们也可以给线程设置分组
Thread(ThreadGroup group, Runnable target, String name)

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int x =0; x<100;x++){
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}
/*
线程组:把多个线程组合到一起
 */
public class ThreadGroupDemo {
    public static void main(String[] args){
       // method1();
        method2();
    }

    public static void method1(){
        MyRunnable my = new MyRunnable();

        Thread t1 = new Thread(my,"林青霞");
        Thread t2 = new Thread(my,"Fairy");

        //线程类里面的方法
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();

        //线程组里面的方法
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        System.out.println(name1);
        System.out.println(name2);
        //通过结果知道:线程默认情况下属于main线程组
        //默认情况下,所有的线程数与同一个组
        System.out.println(Thread.currentThread().getThreadGroup().getName());

        //如何修改线程组:创建一个线程组,把其他线程的组指定为我们自己新建线程组

    }

    public static void method2(){
        ThreadGroup tg = new ThreadGroup("这是一个新的线程组");
        MyRunnable my = new MyRunnable();

        Thread t1 = new Thread(tg,my,"林青霞");
        Thread t2 = new Thread(tg,my,"Fairy");
        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());

        //通过组名称设置后台线程,表示该组的线程都是后台线程
        tg.setDaemon(true);
    }
}

线程池

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int x = 0; x <= 100; x++){
            System.out.println(Thread.currentThread().getName()+  ":" + x);
        }
    }
}
/*
如何实现线程的代码呢?
A:创建一个线程池对象,控制要创建几个线程对象
    public static ExecutorService newFixedThreadPool(int nThreads)
B:这种线程可以执行:
    可以执行Runnable对象或者Callable对象代表的线程
C:调用如下方法即可:
    Future<?> submit(Runnable task)
    <T> Future<T> submit(Callable<T> task)
 */
public class ExecutorsDemo {
    public static void main(String[] args){
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        //结束线程池
        pool.shutdown();
    }
}

callable:好处: 可以有返回值 可以抛出异常弊端:

代码比较复杂,所以一般不用

采用callable接口实现多线程求和案例

public class MyCallable implements Callable<Integer> {
    private int number;
    public MyCallable(int number){
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {

        int sum = 0;
        for (int x =0; x<=number;x++){
            sum += x;
        }
        return sum;
    }
}
public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        Future<Integer> f1 = pool.submit(new MyCallable(100));
        Future<Integer> f2 = pool.submit(new MyCallable(200));

        Integer t1 = f1.get();
        Integer t2 = f2.get();
        System.out.println(t1);
        System.out.println(t2);

        pool.shutdown();
    }
}

匿名内部类方式使用多线程:

new Thread(){代码…}.start();
New Thread(new Runnable(){代码…}).start();

public class ThreadDemo {
    public static void main(String[] args){
        //继承Thread类来实现多线程
        new Thread(){
            public void run(){
                for(int x = 0; x < 100; x ++){
                    System.out.println(Thread.currentThread().getName() + ":" + x);
                }
            }
        }.start();

        //实现Runnable接口来实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int x = 0; x < 100; x ++){
                    System.out.println(Thread.currentThread().getName() + ":" + x);
                }
            }
        }){}.start();
    }
}

定时器:

定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能



































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值