今天记录Java中最重要的一个知识点——多线程!!!

 

进程:就是应用程序在内存中分配的空间。(正在运行中的程序)
    

线程:是进程中负责程序执行的执行单元。也称为执行路径。
    一个进程中至少有一个线程在负责该进程的运行。
    如果一个进程中出现了多个线程,就成该程序为多线程程序。


举例:运动场--鸟巢。水立方。

多线程技术:解决多部分代码同时执行的需求。合理的使用cpu资源。
/*
多线程的运行根据cpu的切换完成的。怎么切换cpu说的算,
所以多线程运行有一个随机性(cpu的快速切换造成的)。

jvm中的多线程。
至少有两个线程:一个是负责自定义代码运行的。这个从main方法开始执行的线程称之为主线程。
            一个是负责垃圾回收的。

通过实验,会发现每次结果不一定相同,因为随机性造成的。

而且每一个线程都有运行的代码内容。这个称之为线程的任务。
之所以创建一个线程就是为了去运行制定的任务代码。

而线程的任务都封装在特定的区域中。
比如:
主线程运行的任务都定义在main方法中。
垃圾回收线程在收垃圾都会运行finalize方法。

*/

class Demo
{
    //定义垃圾回收方法。
    public void finalize()
    {
        System.out.println("demo ok");
    }
}

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

        System.gc();//启动垃圾回收器。
        new Demo();
        new Demo();
        new Demo();
        System.out.println("Hello World!");
    }
}
/*

final , finally , finalize有什么区别?

*/

/*
如何建立一个执行路径呢?

通过查阅api文档 java.lang.Thread类。
该类的描述中有创建线程的两种方式:
1,继承Thread类。
    1,继承Thread类。
    2,覆盖run方法。
    3,创建子类对象就是创建线程对象。
    4,调用Thread类中的start方法就可以执行线程。并会调用run方法。

    start()开启线程后,都会执行run方法。说明run方法中存储的是线程要运行的代码。
    所以,记住,自定义线程的任务代码都存储在run方法中。

*/
class Demo extends Thread
{
    private String name;
    Demo(String name)
    {
        //super();
        this.name = name;
    }

    //覆盖run方法。
    public void run()
    {
        for(int x=1; x<=40; x++)
        {

            System.out.println(this.getName()+"....."+name+"...."+x);
            System.out.println(Thread.currentThread().getName()+"....."+name+"...."+x);
        }
    }
    
}

/*
调用start和调用run方法的区别?
调用start会开启线程,让开启的线程去执行run方法中的线程任务。
直接调用run方法,线程并未开启,去执行run方法的只有主线程。

*/
class  ThreadDemo
{
    public static void main(String[] args) 
    {
        Demo d1 = new Demo("张三");//Thread-0
        Demo d2 = new Demo("麻子");
        d1.start();//start():两件事:1,开启线程,2,调用run方法。
        d2.start();

        for(int x=1; x<40; x++)
        {
            System.out.println(Thread.currentThread().getName()+"--------"+x);
        }
    }
}

 

 

经典实例:卖票

 

class SaleTicket extends  Thread
{
    private  int tickets = 100;//

    //卖票的代码需要被多个线程执行,所以要将这些代码定义在线程任务中。run方法。
    public void  run()
    {
        while(true)
        {
            if(tickets>0)
            {
                System.out.println(Thread.currentThread().getName()+"...."+tickets--);
            }
        }
    }
}


class  TicketDemo
{
    public static void main(String[] args) 
    {
        //创建四个线程。会创建400张票。不合适,不建议票变成静态的,所以如何共享这100张票。需要将资源和线程分离。
            //到api中查阅了第二创建线程的方式。
        SaleTicket t1 = new SaleTicket();
        SaleTicket t2 = new SaleTicket();
        SaleTicket t3 = new SaleTicket();
        SaleTicket t4 = new SaleTicket();
        t1.start();
        t1.start();
        t1.start();
        t1.start();
    }
}

 

/*
创建线程的第二种方式。实现Runnable接口。
1,定义一个类实现Runnable。
2,覆盖Runnable接口中的run方法,将线程要运行的任务代码存储到该方法中。
3,通过Thread类创建线程对象,并将实现了Runnable接口的对象作为Thread类的构造函数的参数进行传递。
4,调用Thread类的start方法,开启线程。



实现Runnble接口的好处:
1,避免了继承Thread类的单继承的局限性。
2,Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
3,Runnable接口出现,降低了线程对象和线程任务的耦合性。
所以,以后创建线程都使用第二种方式。


*/

class SaleTicket implements Runnable
{
    private int tickets = 100;
    public void run()
    {
        while(true)
        {
            if(tickets>0)
            {
                System.out.println(Thread.currentThread().getName()+"...."+tickets--);
            }
        }
    }
}

class TicketDemo2 
{
    public static void main(String[] args) 
    {
        //线程任务对象。
        SaleTicket t = new SaleTicket();

        //创建四个线程。通过Thread类对象。
        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();
        
    }
}

/*
两个储户,到同一个银行存钱,每个人存了3次,一次100元。
1,描述银行。
2,描述储户任务。

分析多线程是否存在安全隐患。

1,线程任务中是否有共享的数据。
2,是否多条操作共享数据的代码。




*/
class Bank
{
    private int sum;
    private Object obj = new Object();
    public void add(int n)
    {
        synchronized(obj)
        {
            sum = sum + n;
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println("sum="+sum);
        }
    }
}
class Customer implements Runnable
{
    private    Bank b = new Bank();
    public void run()
    {
        for(int x=0; x<3; x++)
        {
            b.add(100);
        }
    }
}

class ThreadTest 
{
    public static void main(String[] args) 
    {
        //1,创建任务对象。
        Customer c = new Customer();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}
/*
多线程的安全问题。

产生的原因:
1,线程任务中有处理到共享的数据。
2,线程任务中有多条对共享数据的操作。
一个线程在操作共享数据的过程中,其他线程参与了运算,造成了数据的错误。


解决的思想:
只要保证多条操作共享数据的代码在某一时间段,被一条线程所执行,
在执行期间不允许其他线程参与运算。


咋保证呢?
用到了同步代码块。
synchronized(对象)
{
    需要被同步的代码。
}

同步在目前情况下保证了一次只能有一个线程在执行。其他线程进不来。
这就是同步的锁机制。


好处:解决了多线程的安全问题。

弊端:减低效率。


有可能出现这样一种情况:
多线程安全问题出现后,加入了同步机制,没有想到,安全问题依旧!咋办。

这时肯定是同步出了问题。

只要遵守了同步的前提,就可以解决。

同步的前提:
    多个线程在同步中必须使用同一个锁。这才是对多个线程同步。




*/

class SaleTicket implements Runnable
{
    private int tickets = 100;
    Object obj = new Object();
    public void run()
    {
        while(true)
        {
            synchronized(obj)
            {
                if(tickets>0)
                {    
                    try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                    System.out.println(Thread.currentThread().getName()+"...."+tickets--);
                }
            }
        }
    }
}


class TicketDemo3 
{
    public static void main(String[] args) 
    {
        SaleTicket t = new SaleTicket();

        //创建四个线程。通过Thread类对象。
        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();
    }
}

 

 

分类: JavaJavaSE

头像

小贱贱要飞

理想不只是成为一名程序猿,是成为一名编写人们生活每一步的艺术家!

0 条评论

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据