/* 进程: 正在进行中的程序(直译). 线程: 就是进程中一个负责程序执行的控制单元(执行路径) 一个进程中可以有多个执行路径, 称之为多线程. 一个进程中至少要有一个线程. 开启多个线程是为了同时运行多部分代码. 每一个线程都有自己运行的内容. 这个内容可以称为线程要执行的任务. 多线程的好处: 解决了多部分同时运行的问题. 多线程的弊端: 线程太多回到效率的降低. 其实应用程序的执行都是cpu在做着快速的切换完成的. 这个切换是随机的. jvm启动时就启动了多个线程,至少有两个线程可以分析的出来. 1. 执行main函数的线程 该线程的任务代码都定义在main函数中 2. 负责垃圾回收的线程. */ class Demo extends Object { public void finalize() //用来回收 { System.out.println("demo ok"); } } class ThreadDemo { public static void main(String[] args) { new Demo(); new Demo(); new Demo(); System.gc(); System.out.println("Hello World!"); } } /********************************************/ //多线程的创建方式--继承Thread类 /* 步骤: 1. 定义一个类继承Thread类 2. 覆盖Thread类中的run方法 3. 直接创建Thread的子类对象创建线程 4. 调用start方法开启线程并调用线程的任务run方法执行. 可以通过Thread的getName获取线程的名称Thread-编号(从0开始) 主线程的名字就是main. */ class Demo extends Thread { private String name; Demo(String name) { super(name);//自定义线程的名称 this.name = name; } public void run() { show(); } public void show() { // System.out.println(name+"........x="+x+".......name="+getName()); System.out.println(name+"........x="+x+".......name="+Thread.currentThread().getName());//当前运行线程的名称. } } class ThreadDemo2 { public static void main(String[] args) { /* 创建线程的母的是为了开启一条执行路径, 去运行指定的代码和其他代码实现同时运行. 而运行的指定代码就是这个执行路径的任务. jvm创建的主线程的任务都定义在了主函数中. 而自定义的线程它的任务在哪儿呢? Thread类用于描述线程, 线程是需要任务的, 所以Thread类也对任务的描述. 这个任务就是通过Thread类中的run方法来体现. 也就是说, run方法就是封装自定义线程运行任务的函数. run方法中定义就是线程要运行的任务代码. 开启线程是为了运行指定代码, 所以只有继承Thread类, 并复写run方法. 将运行的代码定义在run方法中即可. */ Demo d1 = new Demo("旺财"); Demo d2 = new Demo("xiaoqiang"); //d1.run(); //d2.run(); d1.start(); //开启线程, 调用run方法 d2.start(); System.out.println("over....."+Thread.currentThread().getName()); } } /*******************************************************************************/ //创建多线程的第二种方式--实现Runnable接口 /* 1. 定义类实现Runnable接口 2. 覆盖接口中的run方法, 将线程的任务代码封装到run方法中. 3. 通过Thread类创建线程对象, 并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递. 为什么? 因为线程的任务都封装在Runnable接口子类对象的run方法中 所以要在线程对象创建时就必须明确要运行的任务. 4. 调用线程对象的start方法开启线程. */ class Demo implements Runnable { public void run() //Runnable里只有一个run方法 { show(); } public void show() { for (int x=0; x<20 ; x++ ) { System.out.println(Thread.currentThread().getName()+"....."+x); } } } class TreadDemo { public static void main(String[] args) { Demo d = new Demo(); Thread t1 = new Thread(d); //将d传进去 Thread t2 = new Thread(d); t1.start(); t2.start(); } } /* 实现Runnable接口的好处: 1. 将线程的任务从线程的子类中分离出来, 进行了单独的封装. 按照面向对象的思想将任务的封装成对象. 2. 避免了java单继承的局限性. 所以, 创建线程的第二种方式较为常用. */ /********************************************************************/ /* 例子: 卖票. 四个人一起卖100张票 */ //第一种方法 class Ticket extends Thread { private static int num = 100; //这个变量要共享, 所以要加静态 public void run() { sale(); } public void sale() { while(true) { if (num>0) { System.out.println(num--); } } } } class TicketDemo { public static void main(String[] args) { Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); } } //第二种方法 class Ticket implements Runnable { private int num = 100; public void run() { sale(); } public void sale() { while(true) { if (num>0) { try{Thread.sleep(10);}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"....."+num--); } } } } class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); //因为只创建了一个对象, 但是下面开启了4个进程, 所以就等于4个人卖共同卖100张票 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(); } } /* 线程安全问题产生的原因: 1. 多个线程在操作共享数据. 2. 操作共享数据的线程代码有多条. 当一个线程在执行操作共享数据的多条代码过程中, 其他线程参与了运算, 就会导致线程安全问题的产生. 解决思路: 就是将多条操作共享数据的线程代码封装起来, 当有线程在执行这些代码的时候, 其他线程是不可以参与运算的. 必须要当前线程把这些代码都执行完毕后, 其他线程才可以参与运算. 在java中用,同步代码块就可以解决这个问题. */ /****************************************************************************/ //同步代码块 /* 格式: synchronized(对象) { 需要被同步的代码 } */ class Ticket implements Runnable { private int num = 100; Object obj = new Object(); public void run() { sale(); } public void sale() { // Object obj = new Object(); //它在这是错误的, 这样每开启一个线程, 就创建一把锁, 一同是4个线程4个锁 while(true) { synchronized(obj) //同步代码块 { if (num>0) { try{Thread.sleep(10);}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"....."+num--); } } } } } class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); 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(); } } /* 同步的好处: 解决了线程的安全问题. 同步的弊端: 相对降低了效率, 因为同步外的线程的都会判断同步锁. 同步的前提: 必须有多个线程并使用同一锁. */ /***********************************************************************/ /* 实例: 储户, 两个, 每个都到银行存钱,每次存100, 共存三次. */ class Bank { private int sum; // private Object obj = new Object(); public synchronized void add(int num) //也可以用同步函数 { // synchronized(obj) // { sum = sum + num; System.out.println("sum="+sum); // } } } class Cus implements Runnable { private Bank b = new Bank(); public void run() { for (int x=0; x<3 ; x++ ) { b.add(100); } } } class BankDemo { public static void main(String[] args) { Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } } /*********************************************************/ //验证同步函数的锁 class Ticket implements Runnable { private int num = 100; Object obj = new Object(); boolean flag = true; public void run() { // Object obj = new Object(); //它在这是错误的, 这样每开启一个线程, 就创建一把锁, 一同是4个线程4个锁 if (flag) { while(true) { synchronized(this) //同步代码块 { if (num>0) { try{Thread.sleep(10);}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"...obj..."+num--); } } } } else while(true) show(); } public synchronized void show() //但是可以这样用, 把需要同步的代码封装起来. { if (num>0) { try{Thread.sleep(10);}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"...function..."+num--); } } } class SynFunctionLockDemo { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{Thread.sleep(10);}catch(InterruptedException e){} t.flag = false; t2.start(); } } /* 同步函数使用的锁是this. 同步函数和同步代码块的区别: 同步函数的锁是固定的this. 同步代码块的锁是任意对象. 建议使用同步代码块. */
原文地址:http://blog.csdn.net/q1q1x2x2/article/details/43059339