java實現多線程有兩種方法:
一種是繼承Thread類
如下面例子:
public class TestThread extends Thread {
//實現多線程方法一:繼承Thread類,并重載run方法
public void run(){
//要將一段代碼在一個新的線程上運行,該代碼應寫在run函數中
while(true){
System.out.println(Thread.currentThread().getName() + "is running...");
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
//啟動一個新的線程,不是直接調用Thread類子類對象的run方法,
//而是調用Thread類子類對象的start方法,Thread類對象的start方法
//將產生新的線程,并在該線程上運行該Thread類對象中的run方法,根據
//多態性 實際上運行的是Thread子類的run方法
TestThread testThread = new TestThread();
testThread.start();
while(true){
System.out.println("main thread is running...");
}
}
}
方法二是:實現接口Runnable中的run函數
如下列所示
//實現多線程方法二:實現Runnable接口,重載run函數,
//大多數情況下,如果只想重寫 run() 方法,而不重寫其他 Thread 方法,
//那么應使用 Runnable 接口。這很重要,
//因為除非程序員打算修改或增強類的基本行為,否則不應為該類創建子類。
public class TestThread implements Runnable {
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "is running......");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
TestThread testThread = new TestThread();//創建TestThread類的一個實例
Thread thread = new Thread(testThread);//創建一個Thread類的實例
thread.start();//使線程進入runnable狀態
while(true){
System.out.println("main thread is running...");
}
}
2.這兩種方法的區別:實現Runnable接口適合多個相同的程序代碼訪問處理同一資源
如下列所示:
//四個售票窗口售同一中車票,總共有100張,要求這四個售票多線程進行
public class TicketRunnable implements Runnable {
private int tickets = 100;
public void run() {
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() +
" is saling ticket " + tickets--);
}
}
}
}
public class TicketRunnableDemo {
public static void main(String[] args){
//創建了四個線程,每個線程調用了同一各TicketRunnable對象中run方法,
//訪問的是同一個對象中的變量tickets,從而滿足了我們的要求。
//實現Runnable接口適合多個相同的程序代碼訪問處理同一資源
TicketRunnable ticketrunnable = new TicketRunnable();
new Thread(ticketrunnable).start();
new Thread(ticketrunnable).start();
new Thread(ticketrunnable).start();
new Thread(ticketrunnable).start();
}
}
而繼承繼承Thread類,并重載run方法不能達到此目的
如下所示:
public class TicketThread extends Thread {
private int tickets = 100;
public void run(){
while(true){
if(tickets>0){
System.out.println(Thread.currentThread().getName() +
"is saling ticket " + tickets--);
}
}
}
}
public class TicketThreadDemo {
public static void main(String[] args){
//我們希望的是四個線程共售100,我們創建四個線程
//但結果與我們希望的相反,各個線程各售100張票
new TicketThread().start();
new TicketThread().start();
new TicketThread().start();
new TicketThread().start();
// 如果我們象下面這樣寫的話,只有一個線程在售票,沒有實現四個線程并發售票
// TicketThread ticketThread = new TicketThread();
// ticketThread.start();
// ticketThread.start();
// ticketThread.start();
// ticketThread.start();
}
}
3.后臺線程
public class DaemonTest {
public static void main(String[] args){
TicketRunnable ticketRunnable = new TicketRunnable();
Thread thread = new Thread(ticketRunnable);
thread.setDaemon(true);//將此線程設置為后臺線程,當進程中只有后臺線程時,進程就會結束
thread.start();
}
}
4.聯合線程
(還沒搞懂)
5. 線程同步
在上面的售票程序中,線程可能是不安全的,
比如我們修改一下:
public class ThreadRunnable implements Runnable{
private int tickets = 100;
public void run() {
while(true){
if(tickets > 0){
try{
Thread.sleep(10);//使該線程暫停,
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName() +
" is saling ticket " + tickets--);
}
}
}
}
再次測試時,發現可能會打印-1 -2 這樣的ticket
所以我們要修改一下,使該程序是線程安全的,
public class TicketSynch implements Runnable{
private int tickets = 100;
String str = new String("");//此對象放在run函數之外
public void run(){
while(true){
synchronized(str){//同步代碼塊
if(tickets > 0){
try{
Thread.sleep(10);
}
catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+" is saling ticket " + tickets--);
}
}
}
}
}
但是 同步以犧牲程序的性能為代價
5. 同步函數
public class SynchMethod implements Runnable{
private int tickets = 100;
public void run(){
while(true){
sale();
}
}
public synchronized void sale(){
if(tickets > 0){
try{
Thread.sleep(10);
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+ "is saling ticket " + tickets--);
}
}
}
public static void main(String[] args){
//創建了四個線程,每個線程調用了同一各TicketRunnable對象中run方法,
//訪問的是同一個對象中的變量tickets,從而滿足了我們的要求。
//實現Runnable接口適合多個相同的程序代碼訪問處理同一資源
SynchMethod synchMethod = new SynchMethod();
new Thread(synchMethod).start();
new Thread(synchMethod).start();
new Thread(synchMethod).start();
new Thread(synchMethod).start();
}
6.代碼塊和函數間的同步
public class MethodAndBlockSyn implements Runnable{
private int tickets = 100;
String str = new String("");
public void run(){
if(str.equals("method")){
while(true){
sale();
}
}
else{
synchronized(str){//同步代碼塊
if(tickets > 0){
try{
Thread.sleep(10);
}
catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+" is saling ticket " + tickets--);
}
}
}
}
public synchronized void sale(){
if(tickets > 0){
try{
Thread.sleep(10);
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+ "is saling ticket " + tickets--);
}
}
}
public static void main(String[] args){
MethodAndBlockSyn test = new MethodAndBlockSyn();
new Thread(test).start();// 這個線程調用同步代碼塊
try{
Thread.sleep(1);//讓線程等待100ms 是為了讓該線程得到CPU
//避免該線程未得到CPU就將str設置為method,從而使此線程也是調用同步函數
}catch(Exception e)
{
}
test.str = new String("method");
new Thread(test).start();//這個線程調用同步函數
}
7. 線程間通訊
wait: 告訴當前線程放棄監視器并進入睡眠狀態,知道有其他線程進入同一監視器并調用notify為止。
notify:喚醒同一對象監視器中調用wait的第一個線程。類似飯館有一個空位置后通知所有等待就餐顧客中的第一位可以入座的情況
notifyAll:喚醒同一對象監視器中調用wait的所有線程,具有最高優先級的程序先被喚醒并執行。
以上三個方法只能在synchronized方法中調用
public class Record {
private String name = "陳世美";
private String sex = "女";
boolean bFull = false;
public synchronized void put(String name, String sex)
{
if(bFull)
try {
wait();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
this.name = name;
try{
Thread.sleep(10);
}
catch(Exception e){
System.out.println(e.getMessage());
}
this.sex = sex;
bFull = true;
notify();
}
public synchronized void get(){
if(!bFull)
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name + "--->" + sex);
bFull = false;
notify();
}
}
public class Productor implements Runnable {
Record record = null;
public Productor(Record record){
this.record = record;
}
public void run(){
int i = 0;
while(true){
if(i==0)
record.put("陳陳陳", "男");
else
record.put("陳世美", "女");
i = (i+1)%2;
}
}
}
public class Consumer implements Runnable {
Record record = null;
public Consumer(Record record){
this.record = record;
}
public void run(){
while(true){
record.get();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Record record = new Record();
new Thread(new Productor(record)).start();
new Thread(new Consumer(record)).start();
}