07-Java基础-多线程 & 单例

2017-07-27  本文已影响0人  xiaohan_zhang

多线程、单例

1、方式一

public class ThreadDemo {
    public static void main (String[] args) {
      // 方式一:继承Thread类
      // 4)在main方法中创建线程对象,并启动线程。
        MyThread myThread = new MyThread();
      // 5)开启线程
        myThread.start();  
        myThread.getName();   //获取当前线程
    }
}

// 1)定义一个类A继承于java.long. Thread类。
class MyThread extends Thread{
// 2)在A类中覆盖Thread的run方法。
    @Override
    public void run() {
        super.run();
// 3)在run方法中编写需要执行的代码。
        for (int i = 0; i < 100; i ++){
        Thread.currentThread().setName("线程---2");  // 设置线程名称
        System.out.println(Thread.currentThread());   // 获取当前线程
        }
    }
}

好处:可以直接使用Thread类中的方法,代码简单。
弊端:如果已经有了父类,就不能用这种方法。
2、方式二

public class ThreadDemo {

    public static void main (String[] args) {
      // 方式二:实现Runnable接口
      // 4)在main方法中创建线程对象,并启动线程。
        Thread thread = new Thread(new GameThread());
        thread.start();
    }
}


// 1)定义一个类A实现java.lang.Runnable接口。
class GameThread implements Runnable{
// 2)在A类中实现Runnable接口中的run方法。
    @Override
    public void run() {
// 3)在run方法中编写需要执行的代码。
        for (int i = 0; i < 100; i ++){
            this.setName("线程---1");  // 设置线程名称
            System.out.println(Thread.currentThread());   // 获取当前线程
        }
    }
}

好处:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的。
弊端:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法。

3、匿名内部类创建线程

public class ThreadDemo {
    public static void main (String[] args) {
        //方式三:匿名内部类
        // 1)将Runnable的子类对象传递给Thread的构造方法
        new Thread(new Runnable() {   
        // 2)重写run方法
            @Override
            public void run() {
        // 3)将要执行的代码写在run方法中
                for (int i = 0; i < 100; i ++){
                    Thread.currentThread().setName("线程---3");  // 设置线程名称
                    System.out.println(Thread.currentThread());   //获取当前线程
                }
            }
        }).start();   // 开启线程
    }
}
new Thread(new Runnable() {
    @Override
    public void run() {
         for (int i = 20; i >= 0; i--){
             System.out.println("倒计时" + i + "秒");
             try {
                 Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
         }
    }
}).start();

特点:若所有的前台线程都死亡,后台线程自动死亡,前台线程没有结束,后台线程是不会结束的。
thread.isDaemon() 返回是否为后台线程。
前台线程创建的线程默认是前台线程,可以通过setDaemon()方法设置为后台线程(setDaemon()方法必须在start方法前调用)。后台线程创建的线程默认为后台线程。

    private static void daemonDemo(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    System.out.println(Thread.currentThread());
                }
            }
        }).start();


        Thread thread1 = new Thread(){
            @Override
            public void run() {
                super.run();
                for (int i = 0; i < 2; i++) {
                    System.out.println(Thread.currentThread());
                }
            }
        };

        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                for (int i = 0; i < 50; i++) {
                    System.out.println(Thread.currentThread());
                }
            }
        };
        thread.setDaemon(true);
        thread.start();
    }
    private static void joinThreadDemo(){
        final Thread thread =  new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println(Thread.currentThread() + "-----111111");
                }
            }
        };

        thread.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    if (i == 2){
                        try {
//                            thread.join();  // 线程1开始执行,执行完后线程2才开始执行
                            thread.join(1); // 线程1执行1毫米,然后线程1和线程2交替执行
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread() + "-----222222");
                }
            }
        }).start();
    }
/**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

// 设置优先级(建议三个常量就可以了)
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Thread.currentThread().setPriority(3);
// 获取当前线程优先级
Thread.currentThread().getPriority()

每个线程都有默认优先级,主线程默认优先级是5;
如果A线程创建了B线程,那么B线程与A线程有相同优先级。

作用不是很明显

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                super.run();
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread() + "-----111");
                }
            }
        };
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                super.run();
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread() + "-----222");
                }
            }
        };

        thread1.setPriority(10);
        thread2.setPriority(1);
        thread1.start();
        thread2.start();
    }
// 同步代码块
synchronized (this) {
      // 多个线程共享的代码    
}
    private static void test(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    PrinterTest.print1();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    PrinterTest.print2();
                }
            }
        }).start();
    }


class PrinterTest {
    ThreadDemo threadDemo = new ThreadDemo();
    public static void print1() {
        synchronized(threadDemo){               //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
            System.out.print("你");
            System.out.print("是");
            System.out.print("猪");
            System.out.print("吗");
            System.out.print("\r\n");
        }
    }

    public static void print2() {
        synchronized(threadDemo){
            System.out.print("无");
            System.out.print("糖");
            System.out.print("口");
            System.out.print("香");
            System.out.print("糖");
            System.out.print("\r\n");
        }
    }
}
    // 同步方法
    synchronized private void doWork () {
        // 多个线程共享的代码
    }
class PrinterTest {
    public static void print1() {
        synchronized(PrinterTest.class){               //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
            System.out.print("你");
            System.out.print("是");
            System.out.print("猪");
            System.out.print("吗");
            System.out.print("\r\n");
        }
    }
    /*
     * 非静态同步函数的锁是:this
     * 静态的同步函数的锁是:字节码对象
     */
    public static synchronized void print2() {
        synchronized(PrinterTest.class){
            System.out.print("无");
            System.out.print("糖");
            System.out.print("口");
            System.out.print("香");
            System.out.print("糖");
            System.out.print("\r\n");
        }
    }
    public synchronized void print3() {
        synchronized(this){
            System.out.print("金");
            System.out.print("钢");
            System.out.print("狼");
            System.out.print("\r\n");
        }
    }
}
/** 
 * 单例模式的实现:饿汉式,线程安全 但效率比较低 
 */  
public class SingletonTest {  
    // 定义一个私有的构造方法
    private SingletonTest() {  
    }  
    // 将自身的实例对象设置为一个属性,并加上Static和final修饰符
    private static final SingletonTest instance = new SingletonTest();  
    // 静态方法返回该类的实例
    public static SingletonTest getInstancei() {  
        return instance;  
    }  
}
/**  
 * 单例模式的实现:懒汉式,线程安全
 */  
public class SingletonTest {
    // 定义私有构造方法(防止通过 new SingletonTest()去实例化)
    private SingletonTest() {   
    }   
    // 定义一个SingletonTest类型的变量(不初始化,注意这里没有使用final关键字)
    private static SingletonTest instance;   
    // 定义一个静态的方法(调用时再初始化SingletonTest,使用synchronized 避免多线程访问时,可能造成重的复初始化问题)
    public static synchronized  SingletonTest getInstance() {   
        if (instance == null)   
            instance = new SingletonTest();   
        return instance;   
    }   
} 
public class Singleton{  
    private static class SingletonHolder{  
        public static Singleton instance = new Singleton();  
    }  
    private Singleton(){
    }  
    public static Singleton getSingleton(){  
        return SingletonHolder.instance;  
    }  
}  
public class Singleton {
    private static volatile Singleton instance = null;
    private Singleton(){}

    public static Singleton getInstance(){
        if (instance == null){
            synchronized(Singleton.class){
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
public enum Singleton {
    INSTACE;
    private String name;
    public String getName() {
    return name;
    }
}
public class TimerDemo {
    public static void main (String[] args) {
        // 3秒后执行
        new Timer().schedule(new MyTimerTask(), 3000);
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("匿名内部类方式");
            }
        },2000);


        // 周期执行
        // 延迟3秒,每1秒执行一次
        new Timer().schedule(new MyTimerTask(), 3000, 1000);
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("每隔1秒定时任务");
            }
        },0,1000);
    }
}


class MyTimerTask extends TimerTask {

    @Override
    public void run() {
        System.out.println("Timer test");
    }
}

wait():执行该方法线程对象释放同步锁,JVM把该线程存放到等待池中,等待其他线程唤醒该线程。
notify():执行该方法的线程唤醒在等待池中等待的任一线程,把线程转到锁池中等待。
notifyAll():执行该方法的线程唤醒在等待池中等待的所有线程,把线程转到锁池中等待。

这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用。

public class ThreadCommunicationDemo {
    public static void main(String[] args){
        MyPrinter myPrinter = new MyPrinter();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    myPrinter.print1();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    myPrinter.print2();
                }
            }
        }).start();

        // 三个线程通信
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    myPrinter.print3();
                }
            }
        }).start();
    }
}

class MyPrinter {
    private int flag = 1;
    public void print1() {
        synchronized(this){               //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
            // 两个线程通信
            /*if (flag != 1){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }*/
            while (flag != 1){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.print("你");
            System.out.print("是");
            System.out.print("猪");
            System.out.print("吗");
            System.out.print("\r\n");
            flag = 2;
            // 两个线程通信
//            this.notify();  // 随机唤醒单个线程

            // 三个线程通信
            this.notifyAll();
        }
    }

    public synchronized void print2() {
        synchronized(this){
            /*if (flag != 2){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }*/
            while (flag != 2){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.print("无");
            System.out.print("糖");
            System.out.print("口");
            System.out.print("香");
            System.out.print("糖");
            System.out.print("\r\n");
            // 两个线程通信
//            flag = 1;
//            this.notify();
            // 三个线程通信
            flag = 3;
            this.notifyAll();
        }
    }

    public synchronized void print3() {
        synchronized(this){
            while (flag != 3){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.print("金");
            System.out.print("钢");
            System.out.print("狼");
            System.out.print("\r\n");
            flag = 1;
            this.notifyAll();
        }
    }
}

如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件。
注意事项:
1.在同步代码块中用哪个对象锁,就用哪个对象调用wait 方法。
2.sleep方法必须传入参数,参数就是时间,时间到了自动醒来,
wait 方法可以传入参数也可以不传入参数。
3.sleep方法在同步函数或同步代码块中,不释放锁;
wait 方法在同步函数或同步代码块中,释放锁。

public class ReentrantLockDemo {
    public static void main(String[] args) {
        final MyPrinter1 myPrinter = new MyPrinter1();

        new Thread() {
            public void run() {
                while(true) {
                    myPrinter.print1();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                while(true) {
                    myPrinter.print2();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                while(true) {
                    myPrinter.print3();
                }
            }
        }.start();
    }

}

class MyPrinter1 {
    private ReentrantLock r = new ReentrantLock();
    private Condition c1 = r.newCondition();
    private Condition c2 = r.newCondition();
    private Condition c3 = r.newCondition();

    private int flag = 1;
    public void print1() {
        r.lock();   // 获取锁
        if(flag != 1) {
            try {
                c1.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("你");
        System.out.print("是");
        System.out.print("猪");
        System.out.print("吗");
        System.out.print("\r\n");
        flag = 2;
        c2.signal();
        r.unlock();     // 释放锁
    }

    public void print2() {
        r.lock();
        if(flag != 2) {
            try {
                c2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("金");
        System.out.print("钢");
        System.out.print("狼");
        System.out.print("\r\n");
        flag = 3;
        c3.signal();
        r.unlock();
    }

    public void print3() {
        r.lock();
        if(flag != 3) {
            try {
                c3.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("无");
        System.out.print("糖");
        System.out.print("口");
        System.out.print("香");
        System.out.print("糖");
        System.out.print("\r\n");
        flag = 1;
        c1.signal();
        r.unlock();
    }
}

public final ThreadGroup getThreadGroup() // 通过线程对象获取它所属于的组
public final String getName() // 通过线程组对象获取他组的名字

也可以给线程设置分组
1.ThreadGroup(String name) 创建线程组对象并给其赋值名字
2.创建线程对象
3.Thread(ThreadGroup?group, Runnable?target, String?name)
4.设置整组的优先级或者守护线程

public class ThreadGroupDemo {
    public static void main(String[] args) {
        threadGroup();
        myThreadGroup();
    }

    public static void threadGroup() {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr, "张三");
        Thread t2 = new Thread(mr, "李四");
        // 获取线程组
        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 myThreadGroup(){
        ThreadGroup tg = new ThreadGroup("我是一个新的线程组");      //创建新的线程组
        MyRunnable mr = new MyRunnable();                               //创建Runnable的子类对象

        Thread t1 = new Thread(tg, mr, "张三");                   //将线程t1放在组中
        Thread t2 = new Thread(tg, mr, "李四");                   //将线程t2放在组中

        System.out.println(t1.getThreadGroup().getName());              //获取组名
        System.out.println(t2.getThreadGroup().getName());

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

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for(int i = 0; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName() + "...." + i);
        }
    }
}

1、新建状态(new)
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

2、就绪状态(ready)
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

3、运行状态(running)
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

4、阻塞状态(blocked)
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
阻塞状态只能先进入就绪状态,不能直接进入运行状态。

5、死亡状态(terminated)
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

/*JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。
*/
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());

//结束线程池
pool.shutdown();
// 动物抽象类
public abstract class Animal {
    public abstract void eat();
}
// 工厂接口
public interface Factory {
    public Animal createAnimal();
}
// 具体狗类
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}
// 具体猫类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
// 狗工厂
public class DogFactory implements Factory {
    @Override
    public Animal createAnimal() {
        return new Dog();
    }
}
// 猫工厂
public class CatFactory implements Factory {
    @Override
    public Animal createAnimal() {
        return new Cat();
    }
}
// 使用
public class Test {
    public static void main(String[] args) {
        DogFactory df = new DogFactory();
        Dog d = (Dog) df.createAnimal();
        d.eat();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读